/**
 * OSAmI: Open Source Ambient Intelligence
 * A simple discovery protocol (based on multicast communications) for constrained devices.
 * Copyright (C) Lionel Debroux 2006 (as a TER/Magistere M1 student in the LSR-IMAG-ADELE team, for a demo in the H-Omega project)
 * Copyright (C) Lionel Debroux 2010 (as an engineer in the LCIS-INPG-CTSYS team, for a pre-demo in the FR part of the OSAMI project)
 * Contact: osami-general@osami-commons.org
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation;
 * version 2.1 of the License.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Suite 500 Boston, MA 02110-1335 USA
 */

package fr.imag.adele.homega.impl.unidiscovery;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Logger;

/**
 * Helper class for encapsulating bridge metadata handling.
 */
/*package*/ class UnidiscoveryBridgeMetadata {

	private HashMap<String,HashMap<String,String>> metadata;
	private HashMap<String,String> addresses;
	private HashMap<String,Integer> ports;

	public UnidiscoveryBridgeMetadata() {
		metadata = new HashMap<String,HashMap<String,String>>();
		addresses = new HashMap<String,String>();
		ports = new HashMap<String,Integer>();
	}

	synchronized void addDevice(String id, String topic, int port) {
		synchronized(metadata) {
			if (!(metadata.containsKey(id))) {
				metadata.put(id, null);
				addresses.put(id, topic);
				ports.put(id, port);
			}
			else {
				Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: adding device, but a device with ID " + id + " already exists !");
			}
		}
	}

	synchronized void removeDevice(String id, String topic, int port) {
		synchronized(metadata) {
			if (metadata.containsKey(id)) {
				if (addresses.get(id).equals(topic) && ports.get(id).equals(port)) {
					metadata.remove(id);
					addresses.remove(id);
					ports.remove(id);
				}
				else {
					Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: removing device, but a device with ID " + id + " exists at a different location !!");
				}
			}
			else {
				Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: removing device, but a device with ID " + id + " does not exist !");
			}
		}
	}

	synchronized void addMetadata(String id, String property, String value) {
		synchronized(metadata) {
			//Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("Adding metadata \"" + property + ":" + value + "\"");
			if (metadata.containsKey(id)) {
				HashMap<String,String> map = metadata.get(id);
				if (map == null) {
					map = new HashMap<String,String>();
				}
				// Add metadata in metadata list.
				map.put(property, value);
				// Replace metadata list associated with this id.
				metadata.put(id,map);
			}
			else {
				Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: adding metadata, but a device with ID " + id + " does not exist !");
			}
		}
	}

	synchronized void removeMetadata(String id, String property) {
		synchronized(metadata) {
			//Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("Removing metadata for \"" + property + "\"");
			if (metadata.containsKey(id)) {
				HashMap<String,String> map = metadata.get(id);
				if (map == null) {
					Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: adding metadata, but a device with ID " + id + " has no metadata !");
				}
				else {
					// Remove metadata from metadata list.
					map.remove(property);
					// Replace metadata list associated with this id.
					metadata.put(id,map);
				}
			}
			else {
				Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: removing metadata, but a device with ID " + id + " does not exist !");
			}
			metadata.remove(property);
		}
	}

	synchronized void parseAddMetadata(String id, String _string) {
		synchronized(metadata) {
			String [] strings;
			String [] strings2;

			strings = _string.split(DeviceMetadata.DEVICE_METADATA_FIELD_SEPARATOR);
			if ((strings.length == 0) ||
				((strings.length == 1) && (strings[0].equals("")))) {
				Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Empty metadata, cannot deploy this device !");
			}
			else {
				for (int i = 0; i < strings.length; i++) {
					strings2 = strings[i].split("\\" + DeviceMetadata.DEVICE_METADATA_KV_SEPARATOR);

					if ((strings2.length != 2) || (strings[i].equals(""))) {
						Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Malformed METADATA \"" + strings[i] + "\"");
					}
					else {
						addMetadata(id, strings2[0], strings2[1]);
					}
				}
			}
		}
	}

	synchronized String getMetadata(String id, String property) {
		synchronized(metadata) {
			String value = null;
			//Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).info("Getting metadata for \"" + property + "\"");
			if (metadata.containsKey(id)) {
				HashMap<String,String> map = metadata.get(id);
				if (map == null) {
					Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: getting metadata, but a device with ID " + id + " has no metadata !");
				}
				else {
					if (map.containsKey(property)) {
						value = map.get(property);
					}
					else {
						Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: getting metadata, a device with ID " + id + " exists but has no property \"" + property + "\" !");
					}
				}
			}
			else {
				Logger.getLogger(Logger.GLOBAL_LOGGER_NAME).warning("Bridge metadata: getting metadata, but a device with ID " + id + " does not exist !");
			}

			return value;
		}
	}

	@Override
	public String toString() {
		synchronized(metadata) {
			String str = new String("");
			Set<Entry<String,HashMap<String,String>>> set = metadata.entrySet();
			Iterator<Entry<String,HashMap<String,String>>> i = set.iterator();
			Entry<String,HashMap<String,String>> e;
			HashMap<String,String> e2;
			Set<Entry<String,String>> set2;
			Iterator<Entry<String,String>> i2;
			Entry<String,String> e3;

			// key1:value1;key2:value2;[...]keyn:valuen;
			while (i.hasNext()) {
				e = i.next();
				str = str + "Metadata for device " + e.getKey() + ":\n";
				e2 = e.getValue();
				if (e2 != null) {
					set2 = e2.entrySet();
					i2 = set2.iterator();
					while (i2.hasNext()) {
						e3 = i2.next();
						str = str + "\t\"" + e3.getKey() + "\" : \"" + e3.getValue() +"\"\n";
					}
				}
				else {
					// A null value means there is no metadata.
					str = str + "\tNONE\n"; 
				}
			}
			return str;
		}
	}
}
